package games.checkers;


import games.checkers.chipPath.pathNode;

import java.util.ArrayList;

public class gameAI 
{
	private checkers program;
	private chipManager manager;
	private checkersBoard checkerb;
	private ArrayList <treeNode> openList = new ArrayList<treeNode>(0);
	private ArrayList <treeNode> closedList = new ArrayList<treeNode>(0);
	
	private int depth;
	public final static int EASY = 3;
	public final static int NORMAL = 5;
	public final static int HARD = 7;
	
	public class treeNode
	{
		private treeNode parent;
		private ArrayList<treeNode> children = new ArrayList<treeNode>(0);
		private int [] board = new int [32];
		private String boardString = "";
		private ArrayList<pathNode> path;
		private byte player;
		private int score;
		private int miniscore;
		private int depth;
		private int number;
		
		public treeNode(treeNode n,int [] b, ArrayList<pathNode> c,byte p)
		{
			parent = n;
			board = b;
			path = c;
			player = p;
			children = new ArrayList<treeNode>(0);
			
			for (int i=0;i<32; i++)
			{
				score += board[i];
				boardString += String.valueOf(board[i])+",";
			}
			if (parent == null) depth = 0; else depth = parent.depth()+1;

//			System.out.println("Depth: "+String.valueOf(depth));
//			System.out.println("Board: "+boardString+" : "+String.valueOf(score));
		}
		
		public void parent(treeNode p){parent = p;}
		public treeNode parent(){return parent;}
		public void children(ArrayList<treeNode> c){children = c;}
		public ArrayList<treeNode> children(){return children;}
		public int[] board(){return board;}
		public String boardString(){return boardString;}
		public byte player(){return player;}
		public ArrayList<pathNode> path(){return path;}
		public void score(int s){score = s;}
		public int score(){return score;}
		public int miniscore(){return miniscore;}
		public void miniscore(int m){miniscore = m;}
		public int depth(){return depth;}
		public int number(){return number;}
		public void number(int n){number = n;}
	}
	
	public gameAI(checkers p)
	{	
		program = p;
		manager = p.chipManager();
	}
	
	public ArrayList<pathNode> generateMove(int[] board,byte player, int d)
	{
		ArrayList<pathNode> move = new ArrayList<pathNode>(0);
		
		depth = d;
		checkerb = program.getBoard();
		
		//make sure that the tree is a fresh one
		openList.clear();
		closedList.clear();
		
		//initialize the tree
		treeNode start = new treeNode(null,board, null, player);
		openList.add(start);
		
		//generate the rest of the tree
		depthLimitExpand(openList.get(0));
		
		//evalutate the tree
		//player1: min, player2: max
		move = minimax();
		
		return move;
	}
	
	private void depthLimitExpand(treeNode node)
	{
		//have I reached the bottom of my tree? if so, return
		if (this.depth == node.depth()) 
		{
			node.miniscore(node.score());
			return;
		}
		
		//if not, then generate children
		ArrayList <treeNode> children = expandNode(node);
		treeNode child;
		
		// did I generate any children? if not, then return
		if (children.size() == 0)
		{
			node.miniscore(node.score());
			return;
		}
		
		//if so, then add them and expand them in order of generation
		int miniscore = 0;
		for (int c=0; c < children.size(); c++)
		{
			child = children.get(c);
			openList.add(child);
			child.number(openList.size());
//			System.out.println("Node: "+child.number()+" Parent: "+child.parent().number()+
//					" Depth: "+child.depth()+" Board: "+child.boardString()+" : "+child.score());
			depthLimitExpand(child);
			
			if (c==0) miniscore = child.miniscore();
			// assess children for minimax score of node
			if (node.player() == chipManager.player1) 
			{
				//choose min
				if (miniscore > child.miniscore()) miniscore = child.miniscore(); 
			}
			else 
			{
				//choose max
				if (miniscore < child.miniscore()) miniscore = child.miniscore();
			}
		}
		node.miniscore(miniscore);
//		System.out.println("NODE "+node.number()+" miniscore: "+node.miniscore());
	}
	
	private ArrayList <treeNode> expandNode(treeNode parent)
	{
		ArrayList <treeNode> children = new ArrayList<treeNode>(0);
		
		// if it is a goal, return
		if (goalTest(parent.board())) 
		{
			return children;
		}
		
		//otherwise find all children
		treeNode child;
		int [] newBoard;
		ArrayList<ArrayList<pathNode>> paths = chipPath.findAllPaths(parent.player(), parent.board(), checkerb, manager);
		//int score = 0;
		for (int p=0;p < paths.size();p++)
		{
			newBoard = applyPath(parent.board,paths.get(p));
			if (boardExists(newBoard)) continue;
			if (parent.player() == chipManager.player1) 
			{
				child = new treeNode(parent,newBoard,paths.get(p),chipManager.player2);
				//if (child.score() > score) score = child.score();
			}
			else 
			{
				child = new treeNode(parent,newBoard,paths.get(p),chipManager.player1);
				//if (child.score() < score) score = child.score();
			}
			children.add(child);
		}
		parent.children(children);
		//parent.score(score);
		return children;
		
	}
	
	private boolean goalTest(int [] board)
	{
		int max = 0;
		int min = 0;
		
		// find max and min
		for (int i=0; i < board.length; i++)
		{
			if (max < board[i]) max = board[i];
			if (min > board[i]) min = board[i];
		}
		
		//if either is zero, GOAL!
		if ((max == 0) || (min == 0)) return true;
		
		return false;
	}
	
	private int [] applyPath(int[] board, ArrayList<pathNode> path)
	{
		int[] newBoard = board.clone();
		pathNode node;
		int chip = 0;
		int tile = 0;
		int prevTile = -1;
		for (int n=0;n<path.size();n++)
		{
			node = path.get(n);
			
			
//			prevTile = node.tile;
			
			switch(node.action)
			{
			case chipPath.start: chip = newBoard[node.tile];  break;
			case chipPath.cell: 
			{
				if ((node.tile < 4) && (chip < 0)) chip = -4;
				if ((node.tile > 27) && (chip > 0)) chip = 4;
				newBoard[node.tile] = chip;
				if (n != 0) newBoard[prevTile] = 0;
				break;
			}
			case chipPath.jumpLeft: newBoard[node.tile] = 0; break;
			case chipPath.jumpRight: newBoard[node.tile] = 0; break;
			case chipPath.end: 
			{
				if ((node.tile < 4) && (chip < 0)) chip = -4;
				if ((node.tile > 27) && (chip > 0)) chip = 4;
				newBoard[node.tile] = chip; break;
			}
			default: break;
			}
//			if (n != 0) newBoard[prevTile] = 0;
			prevTile = node.tile;
			
//			switch(node.action)
//			{
//			case chipPath.start: chip = newBoard[node.tile]; newBoard[node.tile] = 0; break;
//			case chipPath.cell: 
//			{
//					if ((chip < 0) && (node.tile < 4)) chip = -2;
//					if ((chip > 0) && (node.tile > 27)) chip = 2;
////					newBoard[node.tile] = 0;
//					break;
//			}
//			case chipPath.jumpLeft: newBoard[node.tile] = 0; break;
//			case chipPath.jumpRight: newBoard[node.tile] = 0; break;
//			case chipPath.end: newBoard[node.tile] = chip; break;
//			default: break;
//			}
		}
				
		return newBoard;
	}
	
	private boolean boardExists(int[] board)
	{
		int[] openBoard;
		int[] closedBoard;
		int b=0;
		//check the open list
		for (int i=0; i < openList.size(); i++)
		{
			openBoard = openList.get(i).board();
			for (b=31;b>=0;--b)
			{
				if (openBoard[b] != board[b]) return false;
			}
		}
		//check the closed list
		for (int i=0; i < closedList.size(); i++)
		{
			closedBoard = closedList.get(i).board();
			for (b=31;b>=0;--b)
			{
				if (closedBoard[b] != board[b]) return false;
			}
		}
		return true;
	}
	
	private ArrayList<pathNode> minimax()
	{
//		treeNode node ;
//		treeNode outNode = openList.get(0);
//		String scoreString = "";
		
//		for (int i=0; i < openList.size(); i++)
//		{
//			node = openList.get(i);
//			//System.out.println("Depth: "+String.valueOf(node.depth()));
//			//System.out.println("Board: "+node.boardString()+" : "+String.valueOf(node.score()));
//			if (node.depth() == depth) scoreString += String.valueOf(node.score())+",";
//			if (node.depth() == 1) outNode = node;
//		}
//		System.out.println(scoreString);
//		
//		for (int d=depth;d>=0;d-- )
//		{
//			for (int n=0; n < openList.size(); n++)
//			{
//				
//			}
//		}
		
		treeNode parent = openList.get(0);
		treeNode child;
		ArrayList <treeNode> childList = parent.children();
		if (childList.size() == 0) return null;
		treeNode node = childList.get(0);
		int score = 0;
		int sum = 0;
		for (int c=0;c < childList.size();c++)
		{
			child = childList.get(c);
			if (c == 0) 
			{
				score = child.miniscore();
				node = child;
			}
			System.out.println("Child "+child.number()+" miniscore : "+child.miniscore());
			if (parent.player() == chipManager.player1) 
			{
				if (child.miniscore() < score) 
				{
					score = child.miniscore();
					node = child;
				}
			}
			else 
			{
				if (child.miniscore() > score)
				{
					score = child.miniscore();
					node = child;
				}
			}
			sum += score;
		}
		
		//if all the scores are the same, choose random child node.
//		int ave = (int)((float)sum/(float)childList.size());
//		System.out.println("ave: "+ave);
//		if (ave == node.miniscore())
//		{
//			int choice = ((int)(Math.random()*(childList.size()-1)));
//			System.out.println(choice+ " : "+ childList.size());
//			node = childList.get(choice);
//		}
		
		System.out.println("Choice: Node "+node.number()+": "+node.boardString()+" : "+node.score() + " : "+ node.miniscore());
		System.out.println("Start:  Node "+parent.number()+": "+parent.boardString()+" : "+parent.score());
		System.out.println("Player: "+parent.player());
		
		return node.path();
	}
	
}
